package com.googlecode.contests_repo.validation;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.regex.Matcher;

import org.codehaus.jackson.JsonParseException;
import org.codehaus.jackson.map.JsonMappingException;
import org.codehaus.jackson.map.ObjectMapper;

import com.googlecode.contests_repo.core.model.ResourcesUtils;

import eu.vahlas.json.schema.JSONSchema;
import eu.vahlas.json.schema.JSONSchemaProvider;
import eu.vahlas.json.schema.impl.JacksonSchemaProvider;

/**
 * A utility class which contains helper methods that interact with a file
 * system. The class is used in the validation process.
 * 
 * @author joro, petko
 * 
 */
public class ValidationTools {

	static <T> List<String> validateJson(File file, Class<T> classs,
			String validationSchema) {
		List<String> errors = null;

		// Jackson parsing API: the ObjectMapper can be provided
		// and configured differently depending on the application
		ObjectMapper mapper = new ObjectMapper();
		try {
			mapper.readValue(file, classs);
		} catch (JsonParseException e) {
			errors = Arrays.asList("Invalid JSON syntax.");
			return errors;
		} catch (JsonMappingException e) {
			errors = Arrays.asList(e.getMessage());
			return errors;
		} catch (IOException e) {
			errors = Arrays.asList(e.getMessage());
			return errors;
		}

		// Allows to retrieve a JSONSchema object on various sources
		// supported by the ObjectMapper provided
		JSONSchemaProvider schemaProvider = new JacksonSchemaProvider(mapper);

		try {
			// Retrieves a JSON Schema object based on a file
			InputStream schemaIS = new FileInputStream(validationSchema);
			JSONSchema schema = schemaProvider.getSchema(schemaIS);

			// Validates a JSON Instance object stored in a file
			InputStream instanceIS = new FileInputStream(file);
			errors = schema.validate(instanceIS);
			instanceIS.close();
		} catch (Exception e) {
			errors.add("Unknown error occurred.");
		}

		return errors;
	}

	static boolean folderExists(List<String> errors, File folder) {
		// if (errors == null || folder == null) {
		// return false;
		// }

		if (!folder.exists() || !folder.isDirectory()) {
			errors.add(String.format("Folder %s does not exist.",
					folder.getAbsolutePath()));
			return false;
		}
		return true;
	}

	static void checkFolderName(List<String> errors, File folder) {
		// if (errors == null || folder == null) {
		// return;
		// }
		Matcher matcher = ResourcesUtils.RESOURCE_NAME_PATTERN.matcher(folder.getName());
		if (!matcher.matches()) {
			errors.add(String
					.format("Folder %s does not conform to the naming rules given by %s.",
							folder.getAbsolutePath(), ResourcesUtils.RESOURCE_NAME_PATTERN));
		}
	}

	static int countFilesStartingWithPrefix(List<File> fileList,
			String filePrefix, boolean isDirectory) {
		int filesCount = 0;
		for (File file : fileList) {
			if (file.getName().startsWith(filePrefix)
					&& file.isDirectory() == isDirectory) {
				filesCount++;
			}
		}

		return filesCount;
	}

	static boolean containsFolder(List<String> errors, List<File> fileList,
			File folder, String expectedFolderName) {
		boolean hasFolder = false;
		for (File file : fileList) {
			if (file.getName().equals(expectedFolderName)) {
				if (!file.isDirectory()) {
					errors.add(String.format(
							"The folder %s contains a %s file, not a folder.",
							folder.getAbsolutePath(), expectedFolderName));
					break;
				} else {
					hasFolder = true;
					break;
				}
			}
		}

		return hasFolder;
	}

	static boolean containsFile(List<String> errors, List<File> fileList,
			File folder, String expectedFileName) {
		boolean hasFile = false;
		for (File file : fileList) {
			if (file.getName().equals(expectedFileName)) {
				if (file.isDirectory()) {
					errors.add(String.format(
							"The folder %s contains a %s folder, not a file.",
							folder.getAbsolutePath(), expectedFileName));
					break;
				} else {
					hasFile = true;
					break;
				}
			}
		}
		if (hasFile == false) {
			errors.add(String.format(
					"Folder %s does not contain mandatory file %s.",
					folder.getAbsolutePath(), expectedFileName));
		}
		return hasFile;
	}

	static boolean areFileNamesContained(Collection<String> folderNames,
			List<File> fileList) {
		boolean areFilesFound = true;
		for (String folderName : folderNames) {
			boolean isFound = false;
			for (File file : fileList) {
				if (file.getName().equals(folderName)) {
					isFound = true;
					break;
				}
			}
			if (!isFound) {
				areFilesFound = false;
				break;
			}
		}

		return areFilesFound;
	}

	static boolean isFileContainedInFolder(File folder, String fileName,
			boolean isDirectory) {
		if (folder == null || !folder.isDirectory()) {
			return false;
		}

		File file = new File(folder, fileName);
		return file.exists() && file.isDirectory() == isDirectory;
	}
}
